這個例子會示範以 Compositions API 開發一個簡單的圖片輪播。先打 API 從遠端取得資料,之後把資料渲染到畫面,並加入輪播功能,而且輪播功能使用了 Vue 的 transition
來製作。
此例子是參考六角學院的直播裏 Kuro 大大提到的常見面試題之一以及他的例子做法,並加入遠端抓資料(從 random user 抓取 5 筆 user 資料)的功能和客製樣式來改寫。
先展示完成效果:
以下會說明步驟,但只會針對重點部分來解說。
首先,先從 random user 取得資料:
import { reactive } from 'vue';
export default {
setup() {
const users = reactive({ results: [] });
(async () => {
try {
const res = await fetch('https://randomuser.me/api/?results=5');
const resJSON = await res.json();
users.results = resJSON.results;
} catch (err) {
console.log(err);
}
})();
return {
users
};
}
};
在 Vue 3,setup
函式的生命週期等於 created
,因此可以直接在 setup
裏打 API 取資料。或者使用 onMounted
或 onBeforeMount
也可以。
把資料渲染到網頁上,並使用 currentIndex
來指定見前要顯示哪筆資料 :
<!-- 需要加上 v-if 判斷是否已抓到遠端的資料,不然會報錯 -->
<div v-if="users.results[currentIndex]" class="card">
<div class="card-img">
<transition :name="imgTransition">
<img
:key="users.results[currentIndex]"
:src="users.results[currentIndex].picture.large"
/>
</transition>
</div>
<ul>
<li>
{{ users.results[currentIndex].name.title }}
{{ users.results[currentIndex].name.last }}
{{ users.results[currentIndex].name.first }}
</li>
<li>{{ users.results[currentIndex].email }}</li>
<li>{{ users.results[currentIndex].phone }}</li>
</ul>
</div>
注意,最外層需要加上 v-if
來判斷是否已抓到遠端的資料。因為在打 API 取資料是非同步程式。因此,即使我們在 setup 裏已執行打 API的程式碼,但在接收到資料之前,程式會一直繼續執行,包括渲染畫面。當畫面渲染好但資料還沒回來時,就不能在畫面渲染資料,不然會出現例如是 Cannot read property 'name' of undefined
這種錯誤。
transition 的部分會留待最後才解說。
按鈕功能包括:
<div class="carousel-btns">
<a @click.prevent="swipeSlide('prev')" href="#"> prev </a>
<div class="carousel-btn-group">
<a
@click.prevent="selectSlide(index)"
href="#"
v-for="(n, index) in users.results.length"
:key="n"
:class="[
{ 'carousel-btn-active': currentIndex === index },
'carousel-btn'
]"
>
</a>
</div>
<a @click.prevent="swipeSlide('next')" href="#"> next </a>
</div>
setup() {
const imgTransition = ref('');
const currentIndex = ref(0);
// ...
// next, prev 切換資料功能
const swipeSlide = direction => {
imgTransition.value = `${direction}-img`;
if (direction === 'next') {
currentIndex.value = (currentIndex.value + 1) % users.results.length;
} else {
currentIndex.value === 0
? (currentIndex.value = users.results.length - 1)
: currentIndex.value--;
}
};
// 指定顯示單一資料功能
const selectSlide = index => {
if (currentIndex.value > index) {
imgTransition.value = 'prev-img';
} else {
imgTransition.value = 'next-img';
}
currentIndex.value = index;
};
return {
users,
currentIndex,
swipeSlide,
selectSlide,
imgTransition
};
}
關於 next, prev 切換資料功能,如果 direction 是 next,寫法是:
currentIndex.value = (currentIndex.value + 1) % users.results.length;
這寫法是為了當 currentIndex
的數值大於 users
陣列長度時,即代表要切回第一筆資料。currentIndex
是由 0 為開始,這時候會顯示第一筆資料。目前資料長度是 5,因此,當顯示最後一筆資料時, currentIndex
會是 4 。如果再按下 next
, currentIndex
會加一變成 5,而 5 % 5
的餘數會是 0,因此回到第一筆資料。其餘頁碼,例如 1, 2。即是 1 % 5
、2 % 5
等,答案會是 1, 2。即是等於該頁碼的數值。
有兩點要處理:
overflow: hidden
overflow: hidden
切換圖片時,當前一張圖片還沒完全滑走,它就會繼續佔用位置,因此即將要顯示的圖片就會被推擠到下面,情況如下:
因為此例中的img
是被 transition
包著,transition
在切換圖片時,會在舊 img
後再加即將要顯示的新 img
:
切換資料時,使用了 transition 來製作切換動畫。
<div class="card-img">
<transition :name="imgTransition">
<img
:key="users.results[currentIndex]"
:src="users.results[currentIndex].picture.large"
/>
</transition>
</div>
因此新的 img
會被推擠到下面。解決方法就是在 card-img
設定 overflow: hidden
。
setup() {
const swipeSlide = direction => {
imgTransition.value = `${direction}-img`;
// ...
};
const selectSlide = index => {
if (currentIndex.value > index) {
imgTransition.value = 'prev-img';
} else {
imgTransition.value = 'next-img';
}
currentIndex.value = index;
};
}
SCSS:
.prev-img-enter-active,
.prev-img-leave-active,
.next-img-enter-active,
.next-img-leave-active {
transition: all 0.5s;
}
.next-img-leave-to {
transform: translate(-100%);
}
.prev-img-leave-to {
transform: translate(100%);
}
有幾點要注意:
img
,因此要加上 key。<transition name="">
的 name 屬性來定義,因此,當 name 的值是 next-img 時,所有 transition 用到的 class 名稱就會以 next-img
作為前輟,例如是 next-img-leave-active
、next-img-leave-to
。currentIndex
是前進,動畫就會向左滑動。相反的話,就是向右滑動。關於動畫 class 名稱的作用,看此圖就懂了:
截圖自官方文件
因此,當圖片要消失時,如果要設定它的目的地,對應的 class 就是 xxx-leave-to
。
https://codesandbox.io/s/composition-api-tu-pian-lun-bo-x27ht?file=/src/App.vue
setup
、onMounted
、onBeforeMount
裏進行。v-if
避免資料還沒抓到的情況。%
的方式來製作循環的頁碼。transition
切換動畫時,會同時存在舊和新的 DOM,也會為它們加上不同的 class。<transition name="">
的 name 屬性。如沒有指明,預設就會是 v-xxxx
。transition
裏切換的元素有同一標籤,就需使用 key
屬性。呼~ 終於到了最後一天了,感謝團友和讀者,技術分享就到此為止了,明天會總結一下這 30 天的鐵人賽心得,會以個人感受與心得為主,明天見~